from .dataLoader import DataLoader
from .paramsPipe import ParamsPipe
from .portfolio import PortTrack


class Broker:
    """
    处理交易执行逻辑
    1. 交易费用：默认双边按成交金额的 3.5%，最低成本为5元 计算
        * 交易佣金：买卖双向按成交额收取万三，最低5元
        * 交易过户费：成交金额万0.01，双向收取
        * 印花税：卖出收取成交金额的万0.5
        * 交易监管费：万0.02，双向收取
    """

    def __init__(self, data: DataLoader, port: PortTrack, params: ParamsPipe, cash=0):
        self.cash = cash
        self.data = data
        self.check_limit = True
        self.commision_fee = 0.00035  # 双边万分之3.5
        self.noh = 100  # number of hands, 默认按1手=100股进行下单
        self.volume_limit = 0.25
        self.slip = 0.001  # 滑点设置
        self.port_tracker = port  # 设置组合跟踪器

        self.params = params  # 参数传递管线

    def execute(self, code, direction, size, price, limit_type):
        # direction 交易方向：1-买入，-1-卖出
        try:
            _volume = self.data.get_volume(self.params.next_day, code)
        except:
            print(f"获取成交量数据错误，股票代码:{code}, 交易日:{self.params.next_day}")
            _volume = 1.23e+10
        if _volume is None:
            print(f"获取成交量数据为空，采用极大值替代，股票代码:{code}, 交易日:{self.params.next_day}")
            _volume = 1.23e+10

        if self.check_limit:
            _temp = self._check_limit(code, direction, limit_type)
            if _temp:
                # 出现停板信号，则该订单作废
                return 4, None

        # 买入订单执行现金检查
        if direction == 1:
            if self.cash <= 0:
                # 无足够资金下单
                return 3, None
            else:
                # 考虑滑点的实际价格
                _price_with_slip = price * (1 + self.slip)
                if _price_with_slip * self.noh * (1 + self.commision_fee) > self.cash:
                    # 1手都买不起
                    return 3, None

                # 考虑交易费用后的最大可用现金
                if size is None:
                    # 计算最大可购买手数
                    max_affordable = self._calculate_max_affordable(_price_with_slip)
                    size = max_affordable * self.noh
                else:
                    # 按指定数量对应手数下单
                    size = int(size // self.noh) * self.noh
                    if size == 0:
                        # 无足够资金下单
                        return 3, None

                # # 检查现金头寸是否足够下单，若不够则按当前现金所能买到的最大手数下单
                # max_affordable = self._calculate_max_affordable(_price_with_slip)
                # if size > max_affordable * self.noh:
                #     size = max_affordable * self.noh
                #     if size == 0:
                #         # 无足够资金下单
                #         return 3, None
        # 卖出订单执行持仓检查
        if direction == -1:
            if code not in self.port_tracker.holdings_dict:
                return 5, None
            _holding = self.port_tracker.holdings_dict[code]['holding_num']
            if _holding <= 0:
                return 5, None
            if size > _holding:
                # 超过持仓量以持仓量进行卖出
                size = _holding

        # 按成交量限额比例设置交易量
        if size > _volume * self.volume_limit:
            size = int(_volume * self.volume_limit // self.noh) * self.noh

        # 执行订单
        if direction == 1:
            _price = price * (1 + self.slip)
            _cost = _price * size  # 买入花费现金
            # 统一费用计算逻辑
            fee = max(_cost * self.commision_fee, 5)  # 最低费用5元
            if self.cash < _cost + fee:
                # 现金不足，无法继续下单
                return 3, None

            self.cash = self.cash - _cost - fee
            self.port_tracker.update_cash(self.cash)  # 更新组合现金头寸
            self.port_tracker.add_holding(code, size, price)  # 更新最新组合情况
            self.port_tracker.trading_track(self.params.next_day, code, direction, size, price)  # 记录交易流水, 下一交易日
            self.port_tracker.update_portfolio_value()  # 更新组合价值

            return 1, (code, _price, size)
        else:
            _price = price * (1 - self.slip)
            _cost = _price * size  # 卖出花费现金
            fee = max(_cost * self.commision_fee, 5)

            self.cash = self.cash + _cost - fee
            self.port_tracker.update_cash(self.cash)  # 更新组合现金头寸
            self.port_tracker.sell_holding(code, size, price)  # 更新最新组合情况
            self.port_tracker.trading_track(self.params.next_day, code, direction, size, price)  # 记录交易流水
            self.port_tracker.update_portfolio_value()  # 更新组合价值

            return 1, (code, _price, size)

    def _check_limit(self, code, direction, limit_type=0):
        # limit_type: 0-10%涨跌幅限制，1-20%涨跌幅限制
        if limit_type == 0:
            limit_type = 0.098
        else:
            limit_type = 0.198

        _close = self.data.get_close(self.params.today, code)
        _open = self.data.get_open(self.params.next_day, code)
        if _close is None:
            print(f"获取收盘价失败，无法计算是否触及停板规则。股票代码：{code}, 日期: {self.params.today}")
            return False
        if _open is None:
            print(f"获取开盘价失败，无法计算是否触及停板规则。股票代码：{code}, 日期: {self.params.next_day}")
            return False

        # 判断执行成交价是否超过价格限制
        if direction == 1:
            _upper_limit = _close * (1 + limit_type)
            if _open >= _upper_limit:  # 开板涨停，无法继续下单
                return True
            else:
                return False
        else:
            _down_limit = _close * (1 - limit_type)
            if _open <= _down_limit:
                # 开板跌停，无法继续下单
                return True
            else:
                return False

    def _calculate_max_affordable(self, price_with_slip):
        """
        计算考虑佣金和最低费用后的最大可购买手数
        """
        # 先估算可以购买的最大数量（不考虑最低费用）
        estimated_size = self.cash / (price_with_slip * (1 + self.commision_fee))
        estimated_hands = int(estimated_size // self.noh)
        # 检查是否满足最低费用要求
        while estimated_hands > 0:
            cost = price_with_slip * estimated_hands * self.noh
            fee = max(cost * self.commision_fee, 5)
            if cost + fee <= self.cash:
                return estimated_hands
            estimated_hands -= 1
        return 0

    def set_check_limit(self, check_limit: bool):
        # 设置是否检查涨跌停板，默认为True
        self.check_limit = check_limit

    def set_commision_fee(self, commision_fee: float):
        # 设置双边佣金费率，默认为3.5%。
        self.commision_fee = commision_fee

    def set_noh(self, noh: float):
        # 设置一手对应100股
        self.noh = noh

    def set_volume_limit(self, volume_limit: float):
        # 设置单笔成交量限额
        self.volume_limit = volume_limit

    def set_slip(self, slip: float):
        # 设置交易滑点，默认为0.1%
        self.slip = slip
